package service

import (
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/httpmvc/service"
	"gorm.io/gorm"

	"ssalarm/common/dto"
	"ssalarm/common/utils"
	"ssalarm/internal/alarm/models"
	"ssalarm/internal/alarm/models/query"
)

type AlarmAnalyse struct {
	service.Service
}

func (e *AlarmAnalyse) GetAlarmTop(c *query.AlarmReportReq, res map[string]interface{}) error {
	startTime := c.StartTime
	endTime := c.EndTime
	timeQuery := len(startTime) > 0 && len(endTime) > 0
	devAlarmKey := utils.If(timeQuery, "devAlarm", "devAlarm_7DayTop").(string)
	productAlarmKey := utils.If(timeQuery, "productAlarm", "productAlarm_7DayTop").(string)
	if len(c.GetDeptIdList()) == 0 && c.UserId != "1" {
		return errors.New("分组信息不能为空")
	}
	limitSize := utils.If(c.TopSize >= 0, c.TopSize, 10).(int)
	// 默认按设备聚合 (支持按传参字段聚合)
	topType := utils.If(len(c.SortField) == 0, "dev_sn", utils.CamelCaseToUdnderscore(c.SortField)).(string)
	order := dto.OrderDest("count", true)
	if len(topType) == 0 {
		emptyTotal(c, res)
		return nil
	}
	// 告警查询
	if len(c.StartTime) == 0 {
		// 默认查近7日
		c.StartTime = time.Now().AddDate(0, 0, -7).Format("2006-01-02") + " 00:00:00"
	}

	ac := AlarmCount{}
	totalList := make([]AlarmCount, 0)
	if err := ac.AlarmCountByField(e.Orm, &totalList, topType, dto.MakeCondition(c.GetNeedSearch()), order); err != nil {
		return err
	}
	if len(totalList) == 0 {
		emptyTotal(c, res)
		return nil
	}

	devSnList := make([]string, 0)
	prodCodeList := make([]string, 0)
	dm := &models.SysDev{}
	dm.Orm = e.Orm
	devList := make([]models.SysDev, 0)
	pm := &models.SysDevProduct{}
	pm.Orm = e.Orm
	prodList := make([]models.SysDevProduct, 0)

	prodTop := make(map[string]*AlarmCount)
	stationTop := make(map[string]*AlarmCount)
	//告警记录
	devMap := make(map[string]models.SysDev)
	for _, count := range totalList {
		devSnList = append(devSnList, count.Name)
	}
	// 分类统计业务
	if "dev_sn" == topType {
		devNameMap := make(map[string]string)

		// 产品与站点告警Top

		//查询设备
		if c.UserId != "1" {
			_ = dm.FindListByConds(&devList, "dept_id in (?) and sn in (?)", c.GetDeptIdList(), devSnList)
		} else {
			_ = dm.FindListByConds(&devList, "sn in (?)", devSnList)
		}
		for _, dev := range devList {
			devMap[dev.Sn] = dev
			devNameMap[dev.Sn] = dev.Name
			if len(dev.ProductCode) > 0 {
				prodCodeList = append(prodCodeList, dev.ProductCode)
			}
		}
		//groupIdName, _ := commonUtils.GetGroupUtil().GroupIdName(e.Orm)
		groupIdName := make(map[string]string)
		//查询产品、分组
		for _, count := range totalList {
			dev := devMap[count.Name]
			if len(dev.ProductCode) > 0 {
				productName := dev.ProductCode
				if _, ok := prodTop[productName]; !ok {
					prodTop[productName] = &AlarmCount{
						Name: productName,
					}
				}
				prodTop[productName].Count = count.Count
			}

			stationName := ""
			if len(dev.DeptId) > 0 {
				if deptName, ok := groupIdName[dev.DeptId]; ok {
					dev.DeptName = deptName
					stationName = deptName
				}
			}
			if len(stationName) == 0 {
				continue
			}
			if _, ok := stationTop[stationName]; !ok {
				stationTop[stationName] = &AlarmCount{Name: stationName}
			}
			stationTop[stationName].Count += count.Count
		}

		// 所有可见设备对应的产品
		var prodTopResult []AlarmCount
		var stationTopResult []AlarmCount
		if len(devSnList) > 0 {
			//产品top
			_ = pm.FindListByConds(&prodList, "model in (?)", prodCodeList)
			for _, v := range prodTop {
				prodTopResult = append(prodTopResult, *v)
			}
			sort.Sort(AlarmCountList(prodTopResult))

			//站点pop
			for _, v := range stationTop {
				stationTopResult = append(stationTopResult, *v)
			}
			sort.Sort(AlarmCountList(stationTopResult))
		}
		// 补齐TOP差值
		devMore := limitSize - len(totalList)
		productMore := limitSize - len(prodTopResult)
		stationMore := limitSize - len(stationTopResult)
		if devMore > 0 {
			devList := make([]models.SysDev, 0)
			var dw func(db *gorm.DB) *gorm.DB
			if c.UserId != "1" {
				dw = dto.MakeCondition(query.SysDevQuery{DeptIdIn: c.GetDeptIdList(), SnNotIn: devSnList})
			} else {
				dw = dto.MakeCondition(query.SysDevQuery{SnNotIn: devSnList})
			}
			_ = dm.FindList(&devList, dw, dto.Paginate(devMore, 1))
			for _, device := range devList {
				item := AlarmCount{Name: device.Sn, Count: 0}
				totalList = append(totalList, item)
				devNameMap[device.Sn] = device.Name
			}
		}
		if productMore > 0 {
			prodList := make([]models.SysDevProduct, 0)
			pw := dto.MakeCondition(query.SysProductQuery{ProductCodeNin: prodCodeList})
			_ = pm.FindList(&prodList, pw, dto.Paginate(devMore, 1))
			for _, prod := range prodList {
				pdName := prod.Name
				item := AlarmCount{Name: pdName, Count: 0}
				prodTopResult = append(prodTopResult, item)
			}
		}
		if stationMore > 0 {
			//dm := models3.Dept{}
			//deptList := []models3.Dept{}
			//dw := cDto.MakeCondition(query.DeptQuery{
			//	DeptIdList: c.DeptIdList,
			//})
			//_ = dm.FindList(e.Orm, &deptList, dw)
			//for _, dept := range deptList {
			//	//满足top数量
			//	if len(prodTopResult) == limitSize {
			//		break
			//	}
			//	//已经包含机构
			//	if _, ok := stationTop[dept.DeptName]; ok {
			//		continue
			//	}
			//	item := models.AlarmCount{Name: dept.DeptName, Count: 0}
			//	prodTopResult = append(prodTopResult, item)
			//}
		}
		res[devAlarmKey] = totalList
		res[productAlarmKey] = prodTopResult
		res["stationAlarm"] = stationTopResult
		res["devName"] = devNameMap
	} else {
		typeMore := limitSize - len(totalList)
		res[c.SortField] = totalList
		if typeMore < 0 {
			res[c.SortField] = totalList[:limitSize]
		}
	}
	return nil
}

func emptyTotal(c *query.AlarmReportReq, res map[string]interface{}) {
	startTime := c.StartTime
	endTime := c.EndTime
	timeQuery := len(startTime) > 0 && len(endTime) > 0
	devAlarmKey := utils.If(timeQuery, "devAlarm", "devAlarm_7DayTop").(string)
	productAlarmKey := utils.If(timeQuery, "productAlarm", "productAlarm_7DayTop").(string)
	topType := utils.If(len(c.SortField) == 0, "device", c.SortField).(string)
	emptyList := []interface{}{}
	if "device" == topType {
		res[devAlarmKey] = emptyList
		res[productAlarmKey] = emptyList
		res["stationAlarm"] = emptyList
	} else if "alarmType" == topType {
		res["typeAlarm"] = emptyList
	} else if "variableName" == topType {
		res["variableAlarm"] = emptyList
	}
}
func (e *AlarmAnalyse) GetAlarmTrend(c *query.AlarmReportReq, res map[string]interface{}) error {
	timeStamp := c.TimeStamp
	dataType := c.Type
	var startTime string
	var endTime string
	if len(timeStamp) == 0 || !strings.Contains(timeStamp, "~") {
		startTime = time.Now().
			AddDate(0, 0, -7).
			Format("2006-01-02") + " 00:00:00"
		endTime = time.Now().Format("2006-01-02 15:04:05")
	} else {
		dts := strings.Split(timeStamp, "~")
		startTime = dts[0]
		endTime = dts[1]
	}
	c.StartTime = startTime
	c.EndTime = endTime
	am := models.SysAlarm{}
	am.Orm = e.Orm
	alarmList := make([]models.SysAlarm, 0)
	if err := am.FindList(&alarmList, dto.MakeCondition(c.GetNeedSearch())); err != nil {
		return err
	}

	//转换成每天
	dataMap := make(map[string][]models.SysAlarm)
	for _, alarm := range alarmList {
		dts := time.Time(*alarm.AlarmTime).Format("2006-01-02")
		if _, ok := dataMap[dts]; !ok {
			dataMap[dts] = []models.SysAlarm{}
		}
		dataMap[dts] = append(dataMap[dts], alarm)
	}

	start, _ := utils.FormatStrTime(startTime)
	end, _ := utils.FormatStrTime(endTime)
	for start.Unix() < end.Unix() {
		dts := start.Format("2006-01-02 15:04:05")
		tmpMap := make(map[string]interface{})
		day := strings.Split(dts, " ")[0]

		dayAlarmList := dataMap[day]
		if len(dataType) == 0 {
			var total, nonExecute int
			// 总数
			total = len(dayAlarmList)
			for _, alarm := range dayAlarmList {
				if !alarm.ProcessStatus {
					nonExecute += 1
				}
			}
			tmpMap["total"] = total
			tmpMap["nonExecute"] = nonExecute
			tmpMap["executed"] = total - nonExecute
		} else if "1" == dataType {
			devMap := make(map[string]int)
			dc := 0
			for _, alarm := range dayAlarmList {
				//差异设备才算告警
				if _, ok := devMap[alarm.DevSn]; ok {
					continue
				}
				devMap[alarm.DevSn] += 1
				dc += 1
			}
			tmpMap["devCount"] = dc
		} else if "2" == dataType {
			alList := []map[string]interface{}{
				{"key": "1", "value": "提示告警"},
				{"key": "2", "value": "一般告警"},
				{"key": "3", "value": "重要告警"},
				{"key": "4", "value": "严重告警"},
			}
			for _, al := range alList {
				key := fmt.Sprintf("%s%s", "level", al["key"])
				tmpMap[key] = 0
			}

			for _, alarm := range dayAlarmList {
				al := alarm.Level
				key := "level" + strconv.Itoa(al)
				if _, ok := tmpMap[key]; ok {
					tmpMap[key] = (tmpMap[key].(int)) + 1
				}
			}
		}
		start = start.AddDate(0, 0, 1)
		res[day] = tmpMap
	}
	return nil
}

type AlarmCount struct {
	Name  string `json:"_id" gorm:"column:name"`
	Count int64  `json:"count" gorm:"column:count"`
}

func (m *AlarmCount) AlarmCountByField(db *gorm.DB, list *[]AlarmCount, byField string, condition ...func(db *gorm.DB) *gorm.DB) error {
	return db.Model(&AlarmCount{}).
		Select(fmt.Sprintf("%s as name, count(%s) as count", byField, byField)).
		Scopes(condition...).
		Group(fmt.Sprintf("%s", byField)).
		Scan(list).Error
}
func (m *AlarmCount) AlarmCountByConds(db *gorm.DB, list *[]AlarmCount, byField string, condition ...func(db *gorm.DB) *gorm.DB) error {
	return db.Model(&AlarmCount{}).Select(
		fmt.Sprintf("%s as name, count(%s) as count", byField, byField)).
		Scopes().
		Group(fmt.Sprintf("%s", byField)).
		Scan(list).Error
}
func (AlarmCount) TableName() string {
	return "sys_alarm"
}

type AlarmCountList []AlarmCount

func (a AlarmCountList) Len() int {
	return len(a)
}
func (a AlarmCountList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
func (a AlarmCountList) Less(i, j int) bool {
	// 从大到小排序
	return a[i].Count < a[j].Count
}
